#include "current_thread.h"
#include "wrap/execinfo.h"
#include "wrap/unistd.h"

#include <cxxabi.h>
#include <sys/syscall.h>
#include <array>


namespace _ {

static thread_local int tid = 0;
static thread_local std::array<char, 1 << 5> tidString{};
static thread_local int tidStringLength = 6;
static thread_local const char *threadName = nullptr; 

static int thread_id() {
    return static_cast<int>(Wrap::syscall_(SYS_gettid));
}

}


int CurrentThread::tid() {
    if (_::tid == 0) {
        _::tid = _::thread_id();
        _::tidStringLength = ::snprintf(
            _::tidString.data(), _::tidString.size(), "%5d", _::tid);
    }
    return _::tid;
}

const char *CurrentThread::tid_string() {
    return _::tidString.data();
}

int CurrentThread::tid_string_length() {
    return _::tidStringLength;
}

void CurrentThread::set_thread_name(const char *name) {
    _::threadName = name;
}

const char *CurrentThread::thread_name() {
    return _::threadName;
}


std::string CurrentThread::stack_trace(bool demangle) {
    constexpr int FRAME_SIZE = 200;
    std::array<void *, FRAME_SIZE> frame{};
    const auto size = Wrap::backtrace_(frame.data(), FRAME_SIZE);
    auto *const strs  = Wrap::backtrace_symbols_(frame.data(), size);
    if (strs == nullptr) {
        return {};
    }
    std::size_t length = 1 << 8;
    auto *buffer = demangle ? static_cast<char *>(::malloc(length)) : nullptr;
    std::string stacktrace{};
    // skipping this 0th-function 
    for (decltype(+size) i = 1; i < size; ++i) {
        if (demangle) {
            char *leftpar = nullptr;
            char *plus = nullptr;
            for (auto *p = strs[i]; *p != '\0'; ++p) {
                if(*p == '(') 
                    leftpar = p;
                else if (*p == '+') 
                    plus = p;
            }
            if (leftpar != nullptr && plus != nullptr) {
                *plus = '\0';
                auto status = 0;
                auto *const result = abi::__cxa_demangle(
                    leftpar + 1, buffer, &length, &status);
                *plus = '+';
                if (status == 0) {
                    buffer = result;
                    stacktrace.append(strs[i], leftpar + 1);
                    stacktrace.append(buffer);
                    stacktrace.append(plus);
                    stacktrace.push_back('\n');
                    continue;
                }
            }
        }
        stacktrace.append(strs[i]);
        stacktrace.push_back('\n');
    }
    ::free(buffer);
    ::free(strs);
    
    return stacktrace;
}